package rubix.view {
	import com.adobe.scenegraph.MaterialStandard;
	import com.adobe.scenegraph.MeshUtils;
	import com.adobe.scenegraph.SceneMesh;
	import com.adobe.scenegraph.SceneNode;
	import flash.geom.Matrix3D;
	
	import flash.geom.Vector3D;
	
	import rubix.data.Sides;
	import rubix.events.CubeEvent;
	
	public class RubixView extends SceneNode {
		private var _cubes:Vector.<SceneMesh>; // вектор с маленькими кубиками
		private var highLightMaterial:MaterialStandard; // материал подсветки
		private var blackMaterial:MaterialStandard;
		public static const ORIGIN:Vector3D = new Vector3D();
		
		public function RubixView(cubes:Vector.<Array>){
			super("Rubix");
			initMaterials();
			initModels(cubes);
		}
		
		private function initMaterials():void {
			blackMaterial = new MaterialStandard("black");
			blackMaterial.diffuseColor.setFromUInt(0x333333);
			blackMaterial.specularColor.setFromUInt(0xffffff);
			highLightMaterial = new MaterialStandard("model");
			highLightMaterial.diffuseColor.setFromUInt(0x66ff33);
			highLightMaterial.specularColor.setFromUInt(0xffffff);
		}
		
		private function initModels(cubesData:Vector.<Array>):void {
			_cubes = new Vector.<SceneMesh>();
			var cx:int, cy:int, cz:int;
			for (var i:int = 0; i < cubesData.length; i++){
				cx = cubesData[i][0];
				cy = cubesData[i][1];
				cz = cubesData[i][2];
				var cube:SceneMesh = new SmallCube(2.5, 0.2, 0.2, blackMaterial); // MeshUtils.createSuperSphere(2.5, 0.2, 0.2, blackMaterial);//new SceneMesh();
				
				_cubes.push(cube);
				cube.name = "cube" + i;
				cube.setPosition(cx * 5, cy * 5, cz * 5);
				cube.userData = {};
				// Добавляем накладки на кубик
				// Если накладка не нужна - для нее не найдется материала и она не создастся (см. createPlane)
				createPlane([0, cy, 0], cube);
				createPlane([cx, 0, 0], cube);
				createPlane([0, 0, cz], cube);
				
				addChild(cube);
			}
		}
		
		// Вспомагательный метод для создания цветных накладок на грани кубиков
		// orientation - массив, кодирующий положении грани на кубике
		// cube - фигура, на которую нужно добавить грань
		private function createPlane(orientation:Array, cube:SceneNode):void {
			var material:MaterialStandard = Sides.getMaterialByOrientation(orientation);
			if (!(material is MaterialStandard))
				return;
			// Размер накладки на грань
			var pw:Number = 4;
			// Сама накладка создается как кубик...
			var plane:SceneMesh = MeshUtils.createCube(1, material); //MeshUtils.createPlane(pw,pw,20,20,material);
			//plane.pickable = false;
			// ...а потом растягивается в нужных пропорциях...
			plane.appendScale.apply(null, orientation.map(function(item:*, ... rest):* {
					return (item == 0) ? pw : 0.1;
				}));
			// ...и смещается в нужном направлении
			plane.appendTranslation.apply(null, orientation.map(function(item:*, ... rest):* {
					return item * 2.5;
				}));
			cube.addChild(plane);
		}
		
		public function onAnimate(t:Number, dt:Number):Boolean {
			var animationComplete:Boolean = false;
			function animateCube(item:SceneMesh, ... rest):void {
				if (!isNaN(item.userData.rotation)){
					var r:Number = item.userData.rotation * 0.3;
					if (Math.abs(r) < 3){
						r = item.userData.rotation;
						item.userData.rotation = Number.NaN;
						animationComplete = true;
					} else {
						item.userData.rotation -= r;
					}
					item.appendRotation(r, item.userData.axis, ORIGIN);
				}
			
			}
			function testComplete(item:SceneMesh, ... rest):Boolean {
				return isNaN(item.userData.rotation) || (item.userData.rotation == 0);
			}
			_cubes.forEach(animateCube);
			
			return animationComplete && _cubes.every(testComplete);
		}
		
		// Слушатели модели
		public function modelEventsHandler(e:CubeEvent):void {
			var item:SceneMesh = _cubes[e.cubeID];
			var key:*;
			switch (e.type){
				case CubeEvent.CUBE_ROTATED:  {
					var iud:* = item.userData;
					// Принудительно докручиваем кубик в нужное положение
					if (!isNaN(iud.rotation)){
						item.appendRotation(iud.rotation, iud.axis, ORIGIN);
					}
					// Запоминаем нужное вращение для анимации.
					iud.rotation = e.degrees;
					iud.axis = e.axis;
					break;
				}
				case CubeEvent.CUBE_HIGHLIGHTED:  {
					if (item.elements.length <= 0)
						break;
					key = item.elements[0];
					item.materialBindings[key] = highLightMaterial;
					break;
				}
				case CubeEvent.CUBE_UNHIGHLIGHTED:  {
					if (item.elements.length <= 0)
						break;
					key = item.elements[0];
					item.materialBindings[key] = blackMaterial;
					break;
				}
				default:  {
					break;
				}
			}
		}
	}
}